#pragma once

namespace LSH
{

template<typename T>
class Array
{
public:
    class const_iterator
    {
        friend class Array;
    public:
        const_iterator():_index(0)
        {
        }
        const_iterator(Array* pArray, int index):
            _pArray(pArray),
            _index(index)
        {
        }

        const_iterator& operator++ ()
        {
            _index++;
            return *this;
		}

        const_iterator operator++ ()
        {
            const_iterator itr(_pArray, _index);
            _index++;
            return itr;
        }
		
        const_iterator& operator-- ()
        {
            _index--;
            return *this;
        }
        
        const_iterator operator--()
        {
            const_iterator itr(_pArray, _index);
            _index--;
            return itr;
        }

        const T& operator* () const
        {
            return (*_pArray)[_index];
        }

        bool operator== (const const_iterator& src) const
        {
            return(_index == src._index);
        }
        
        bool operator!= (const const_iterator& src) const
        {
            return (_index != src._index);
        }
        
        bool operator> (const const_iterator& src) const
        {
            return(_index > src._index);
        }
        
        bool operator< (const const_iterator& src) const
        {
            return (_index < src._index);
        }
        
        bool operator>= (const const_iterator& src) const
        {
            return(_index >= src._index);
        }
        
        bool operator<= (const const_iterator& src) const
        {
            return (_index <= src._index);
        }

        int GetIndex() { return _index };
    protected:
        Array*	_pArray;
        int		_index;
    };
    
    class iterator : public const_iterator
    {
        friend class Array;
    public:
        iterator():const_iterator()
        {
        }
        
        iterator(Array* pArray, int index):
            _pArray(pArray),
            _index(index)
        {
        }
        
        T& operator* ()
        {
            return (*_pArray)[_index];
        }

    protected:
        Array*	_pArray;
        int		_index;
    };

    Array():
        _numItems(0),
        _numReserved(0),
        _pTable(0),
        _incrementSize(1)
    {
    }

    ~Array() { Clear(); }

    int Size()  { return _numItems; }

    int Capacity() { return _numReserved; }

    bool Empty() { return (_numItems == 0); }

    int SetIncrementSize(int incrementSize) { _incrementSize = incrementSize; }

    int GetIncrementSize() { return incrementSize; }

    bool PushBack(T date)
    {
        if(_numItems >= _numReserved)
        {
            if(false == innerAlloc(_numReserved + incrementSize, true))
            {
                return false;
            }
        }
        _pArray[_numItems] = date;
        _numItems++;
        return true;
    }

    bool PushFront()
    {
        if(_numItems >= _numReserved)
        {
            if(false == innerAlloc(_numReserved + incrementSize, true))
            {
                return false;
            }
        }
        shiftDate(0, 1);

        _pArray[0] = date;
        _numItems++;
        return true;
    }

    bool Remove(T date)
    {
        int i = _numItems - 1;
        while(i >= 0)
        {
            if(_pTable[i] == date)
            {
                shiftData(i+1, -1);
                _numItems--;
                i -=2;
            }else{
                i--;
            }
        }
        return true;
    }

    iterator Erase(iterator& itr)
    {
       shiftData(itr._index + 1, -1);

       iterator nextItr(this, itr._index);
       return nextItr;
    }

    iterator Begin()
    {
        iterator itr(this, 0);
        return itr;
    }

    const_iterator Begin() const
    {
        const_iterator itr(this, 0);
        return itr;
    }

    iterator End()
    {
        iterator itr(this, _numItems);
        return itr;
    }

    const_iterator End() const
    {
        const_iterator itr(this, _numItems);
        return itr;
    }

    T& operator[] (int index)
    {
        assert(index < _numItems);
        return _pArray[index];
    }

    const T& operator[] (int index) const
    {
        assert(index < _numItems);
        return _pArray[index];
    }

    operator T* ()
    {
        return _pArray;
    }

    operator const T* ()
    {
        return _pArray;
    }

    bool Reserve(int reserveSize)
    {
        if (false == innerAlloc(reserveSize, true))
        {
            return false;
        }

        if (_numReserved < _numItems)
        {
            m_nNumItems = _numReserved;
        }
        return true;
    }

    bool Resize(int newSize)
    {
        if(_numItems < newSize)
        {
            if(false == innerAlloc(newSize, true))
            {
                return false;
            }
        }
        _numItems = newSize;
        return true;
    }

    void operator = (const Array<T>& src)
    {
        if(src.Capacity() > 0)
        {
            innerAlloc(src.Capacity(), false);
            memcpy(_pTable, src._pTable, sizeof(Type) * _numReserved);
        }
        _numItems = src.size();
    }

    bool Clear()
    {
        innerDealloc();
        return true;
    }


protected:
    bool innerAlloc(int newSize, bool preserve){
        if(_numReserved == newSize)
        {
            return true;
        }

        if(newSize == 0)
        {
            innerDealloc();
            return true;
        }

        int copySize = _numItems;
        if(copySize > newSize)
        {
            copySize = newSize;
        }

        T* pNewTable = (T*)new(newSize * sizeof(T));
        for(n = 0; n < newSize; n++)
        {
            pNewTable[n] = T();
        }

        if(preserve)
        {
            for(n = 0; n < copySize; n++)
            {
                pNewTable[n] = _pTable[n];
            }
        }

        innerDealloc();
        _pTable = pNewTable;
        _numItems = copySize;
        _numReserved = newSize;

        return true;
    }

    void innerDealloc()
    {
        if(NULL != _pTable)
        {
            delete(_pTable);
            _numItems = 0;
            _numReserved = 0;
            _pTable = NULL;
        }
    }

    bool shiftData(int startIndex, int direction)
    {
        if (direction > 0)
        {
            assert((_numItems + direction) <= _numReserved);
            for (int n = _numItems - 1; n >= startIndex; n--)
            {
                _pTable[n + direction] = m_pTable[n];
            } 
        } 
        else
        {
            assert((_numItems + direction) >= 0);
            for (int n = startIndex; i< _numItems; i++)
            {
                _pTable[n + direction] = m_pTable[n];
            }
        }
        return true;
    }

protected:
    int     _numItems;
    int     _numReserved;
    int     _incrementSize;
    T*      _pTable;
};

} // LSH
